(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.monitorSdk = {}));
})(this, (function (exports) { 'use strict';

  const cache = [];
  function getCache() {
    return cache;
  }
  function addCache(data) {
    cache.push(data);
  }
  function clearCache() {
    cache.length = 0;
  }

  let timer = null;

  /**
   * 上报
   * @param {*} type 
   * @param {*} params 
   */
  function lazyReport(type, params) {
    const appId = window['_monitor_app_id_'];
    const userId = window['_monitor_user_id_'];
    const delay = window['_monitor_delay_'];
    const logParams = {
      appId,
      // 项目的appId
      userId,
      // 用户id
      type,
      // error/action/visit/user
      data: params,
      // 上报的数据
      currentTime: new Date().getTime(),
      // 时间戳
      currentPage: window.location.href,
      // 当前页面
      ua: navigator.userAgent // ua信息
    };
    let logParamsString = JSON.stringify(logParams);
    addCache(logParamsString);
    const data = getCache();
    if (delay === 0) {
      // delay=0相当于不做延迟上报
      report(data);
      return;
    }
    if (data.length > 10) {
      report(data);
      clearTimeout(timer);
      return;
    }
    clearTimeout(timer);
    timer = setTimeout(() => {
      report(data);
    }, delay);
  }
  function report(data) {
    const url = window['_monitor_report_url_'];

    // ------- fetch方式上报 -------
    // 跨域问题
    // fetch(url, {
    //   method: 'POST',
    //   body: JSON.stringify(data),
    //   headers: {
    //     'Content-Type': 'application/json',
    //   },
    // }).then(res => {
    //   console.log(res);
    // }).catch(err => {
    //   console.error(err);
    // })

    // ------- navigator/img方式上报 -------
    // 不会有跨域问题
    if (navigator.sendBeacon) {
      // 支持sendBeacon的浏览器
      navigator.sendBeacon(url, JSON.stringify(data));
    } else {
      // 不支持sendBeacon的浏览器
      let oImage = new Image();
      oImage.src = `${url}?logs=${data}`;
    }
    clearCache();
  }

  /**
   * 手动上报
   */
  function tracker(actionType, data) {
    lazyReport('action', {
      actionType,
      data
    });
  }

  /**
   * 自动上报
   */
  function autoTrackerReport() {
    // 自动上报
    document.body.addEventListener('click', function (e) {
      const clickedDom = e.target;

      // 获取标签上的data-target属性的值
      let target = clickedDom?.getAttribute('data-target');

      // 获取标签上的data-no属性的值
      let no = clickedDom?.getAttribute('data-no');
      // 避免重复上报
      if (no) {
        return;
      }
      if (target) {
        lazyReport('action', {
          actionType: 'click',
          data: target
        });
      } else {
        // 获取被点击元素的dom路径
        const path = getPathTo(clickedDom);
        lazyReport('action', {
          actionType: 'click',
          data: path
        });
      }
    }, false);
  }

  /**
   * history路由监听
   */
  function historyPageTrackerReport() {
    let beforeTime = Date.now(); // 进入页面的时间
    let beforePage = ''; // 上一个页面

    // 获取在某个页面的停留时间
    function getStayTime() {
      let curTime = Date.now();
      let stayTime = curTime - beforeTime;
      beforeTime = curTime;
      return stayTime;
    }

    /**
     * 重写pushState和replaceState方法
     * @param {*} name 
     * @returns 
     */
    const createHistoryEvent = function (name) {
      // 拿到原来的处理方法
      const origin = window.history[name];
      return function (event) {
        // if (name === 'replaceState') {
        //   const { current } = event;
        //   const pathName = location.pathname;
        //   if (current === pathName) {
        //     let res = origin.apply(this, arguments);
        //     return res;
        //   }
        // }

        let res = origin.apply(this, arguments);
        let e = new Event(name);
        e.arguments = arguments;
        window.dispatchEvent(e);
        return res;
      };
    };

    // history.pushState
    window.addEventListener('pushState', function () {
      listener();
    });

    // history.replaceState
    window.addEventListener('replaceState', function () {
      listener();
    });
    window.history.pushState = createHistoryEvent('pushState');
    window.history.replaceState = createHistoryEvent('replaceState');
    function listener() {
      const stayTime = getStayTime(); // 停留时间
      const currentPage = window.location.href; // 页面路径
      lazyReport('visit', {
        stayTime,
        page: beforePage
      });
      beforePage = currentPage;
    }

    // 页面load监听
    window.addEventListener('load', function () {
      // beforePage = location.href;
      listener();
    });

    // unload监听
    window.addEventListener('unload', function () {
      listener();
    });

    // history.go()、history.back()、history.forward() 监听
    window.addEventListener('popstate', function () {
      listener();
    });
  }

  /**
   * hash路由监听
   */
  function hashPageTrackerReport() {
    let beforeTime = Date.now(); // 进入页面的时间
    let beforePage = ''; // 上一个页面

    function getStayTime() {
      let curTime = Date.now();
      let stayTime = curTime - beforeTime;
      beforeTime = curTime;
      return stayTime;
    }
    function listener() {
      const stayTime = getStayTime();
      const currentPage = window.location.href;
      lazyReport('visit', {
        stayTime,
        page: beforePage
      });
      beforePage = currentPage;
    }

    // hash路由监听
    window.addEventListener('hashchange', function () {
      listener();
    });

    // 页面load监听
    window.addEventListener('load', function () {
      listener();
    });
    const createHistoryEvent = function (name) {
      const origin = window.history[name];
      return function (event) {
        // if (name === 'replaceState') {
        //   const { current } = event;
        //   const pathName = location.pathname;
        //   if (current === pathName) {
        //     let res = origin.apply(this, arguments);
        //     return res;
        //   }
        // }

        let res = origin.apply(this, arguments);
        let e = new Event(name);
        e.arguments = arguments;
        window.dispatchEvent(e);
        return res;
      };
    };
    window.history.pushState = createHistoryEvent('pushState');

    // history.pushState
    window.addEventListener('pushState', function () {
      listener();
    });
  }

  /**
   * 全局错误捕获
   */
  function errorTrackerReport() {
    // --------  js error ---------
    const originOnError = window.onerror;
    window.onerror = function (msg, url, row, col, error) {
      // 处理原有的onerror
      if (originOnError) {
        originOnError.call(window, msg, url, row, col, error);
      }
      // 错误上报
      lazyReport('error', {
        message: msg,
        file: url,
        row,
        col,
        error,
        errorType: 'jsError'
      });
    };

    // ------  promise error  --------
    window.addEventListener('unhandledrejection', error => {
      lazyReport('error', {
        message: error.reason,
        error,
        errorType: 'promiseError'
      });
    });

    // ------- resource error --------
    window.addEventListener('error', error => {
      let target = error.target;
      let isElementTarget = target instanceof HTMLScriptElement || target instanceof HTMLLinkElement || target instanceof HTMLImageElement;
      if (!isElementTarget) {
        return; // js error不再处理
      }
      lazyReport('error', {
        message: "加载 " + target.tagName + " 资源错误",
        file: target.src,
        errorType: 'resourceError'
      });
    }, true);
  }

  /**
   * 手动捕获错误
   */
  function errorCaptcher(error, msg) {
    // 上报错误
    lazyReport('error', {
      message: msg,
      error: error,
      errorType: 'catchError'
    });
  }

  /**
   * 加载配置
   * @param {*} options 
   */
  function loadConfig(options) {
    const {
      appId,
      // 系统id
      userId,
      // 用户id
      reportUrl,
      // 后端url
      autoTracker,
      // 自动埋点
      delay,
      // 延迟和合并上报的功能
      hashPage,
      // 是否hash录有
      errorReport // 是否开启错误监控
    } = options;

    // --------- appId ----------------
    if (appId) {
      window['_monitor_app_id_'] = appId;
    }

    // --------- userId ----------------
    if (userId) {
      window['_monitor_user_id_'] = userId;
    }

    // --------- 服务端地址 ----------------
    if (reportUrl) {
      window['_monitor_report_url_'] = reportUrl;
    }

    // -------- 合并上报的间隔 ------------
    if (delay) {
      window['_monitor_delay_'] = delay;
    }

    // --------- 是否开启错误监控 ------------
    if (errorReport) {
      errorTrackerReport();
    }

    // --------- 是否开启无痕埋点 ----------
    if (autoTracker) {
      autoTrackerReport();
    }

    // ----------- 路由监听 --------------
    if (hashPage) {
      hashPageTrackerReport(); // hash路由上报
    } else {
      historyPageTrackerReport(); // history路由上报
    }
  }

  /**
   * 获取元素的dom路径
   * @param {*} element 
   * @returns 
   */
  function getPathTo(element) {
    if (element.id !== '') return '//*[@id="' + element.id + '"]';
    if (element === document.body) return element.tagName;
    let ix = 0;
    let siblings = element.parentNode.childNodes;
    for (let i = 0; i < siblings.length; i++) {
      let sibling = siblings[i];
      if (sibling === element) return getPathTo(element.parentNode) + '/' + element.tagName + '[' + (ix + 1) + ']';
      if (sibling.nodeType === 1 && sibling.tagName === element.tagName) ix++;
    }
  }

  //*[@id="root"]/DIV[1]/DIV[2]/BUTTON[1]

  /**
   * 初始化配置
   * @param {*} options 
   */
  function init(options) {
    // ------- 加载配置 ----------
    // 1.拿到配置信息 
    // 2.注入监控代码
    console.log("监控sdk已启动");
    loadConfig(options);

    // -------- uv统计 -----------
    lazyReport('user', '加载应用');

    // ------ 防止卸载时还有剩余的埋点数据没发送 ------
    window.addEventListener('unload', () => {
      const data = getCache();
      report(data);

      // if (data.length > 0) {
      //   report(data);
      // }
    });
  }

  exports.errorCaptcher = errorCaptcher;
  exports.init = init;
  exports.tracker = tracker;

  Object.defineProperty(exports, '__esModule', { value: true });

}));
